#!/bin/bash
# vim:ts=4:sw=4:expandtab

############################################################
# NAME
#     mkd - a toy for make and deploy targets.
#
# SYNOPSIS
#     mkd
#     mkd [ options ] [ targets ] ...
#
#     debug=on mkd ...
#
# OPTIONS
#       -C DIRECTORY  Change to DIRECTORY before doing anything.
#       -f FILE       Read FILE as a makefile.
#       -h            Print this message and exit.
#       -j [N]        Allow N jobs at once; infinite jobs with no arg.
#       -l [N]        Don't start multiple jobs unless load is below N.
#
#       -m MODULENAME Use MODULENAME as module-name for each target.
#       -d DEPLOYPATH Use DEPLOYPATH as deploy-path for each target.
#
#       -x            Don't use mkx makefile.
#       -z            Don't use make-flags config.
#
# AUTHORS
#     neiku project <ku7d@qq.com> 
#
# SEE ALSO
#     mkxrc_targets
#     mkxrc_modules
#     mkxrc_configs
#     mkxrc_targetregs
#
# VERSION
#     2015/11/21: 支持命令行或者makefile中OUTPUT定义的target
#                 支持-f选项指定自定义makefile
#                 支持-C选项指定自定义make目录
#     2015/11/22: 支持绝对/相对目录递归mkd
#     2015/11/25: 支持部署路径跟着target配置走，独立于module
#                 支持可配置登录方式(目前只支持rsa)
#                 支持自定义make前/后执行命令(pre/post-make)
#                 支持deploy前/后执行命令(pre/post-deploy)
#     2015/11/26: 支持忽略未配置target
#     2015/11/28: 支持使用mkm查找target(project/global/system级别)
#                 支持使用mkm查找module(project/global/system级别)
#                 支持使用mkm查找config(project/global/system级别)
#     2015/12/03: 支持本地部署模块(local module)
#     2015/12/05: 支持基于密码登录的远程部署模块(passwd module)
#     2015/12/06: 支持非make编译的target(例如配置文件)
#     2015/12/11: 支持使用mk工具编译target
#     2015/12/23: 支持模式匹配target
#     2015/12/26: 支持忽略pre/post-deploy命令错误信息
#     2016/01/29: 支持透传make -j选项(并行编译)
#     2016/02/16: 支持自定义默认target变量名(key => output-name)
#                 (默认使用OUTPUT做为默认target变量名)
#     2016/02/17: 支持自定义选项(make选项集的子集 + mkx扩展)
#                 支持-x临时不使用mkx makefile(透传mk)
#                 支持-z临时不使用make-flags配置(透传mk)
#     2016/04/14: 支持命令行指定module-name(-m)、deploy-path(-d)
#                 (方便临时切换部署环境)
#     2016/05/01: 支持透传-m/-d选项给mkrun
#                 支持自定义module(module type = custom)
#                 (custom-deployer 配置自定义部署工具)
#
############################################################

# target(*) <-----> module(1) <-----> deploy destination(1)

# help
function help()
{
    echo "Usage: mkd [ options ] [ targets ] ..."
    echo "Options:"
    echo "  -C DIRECTORY  Change to DIRECTORY before doing anything."
    echo "  -f FILE       Read FILE as a makefile."
    echo "  -j [N]        Allow N jobs at once; infinite jobs with no arg."
    echo "  -h            Print this message and exit."
    echo "  -l [N]        Don't start multiple jobs unless load is below N."
    echo ""
    echo "  -m MODULENAME Use MODULENAME as module-name for each target."
    echo "  -d DEPLOYPATH Use DEPLOYPATH as deploy-path for each target."
    echo ""
    echo "  -x            Don't use mkx makefile."
    echo "  -z            Don't use make-flags config."
    echo ""
    echo "Report bugs to <ku7d@qq.com>"
}

# mkd -C {cmdline_makedir} -f {cmdline_makefile} {cmdline_options} {cmdline_targets}
cmdline_makedir="`pwd`"
cmdline_makefile=""
cmdline_options=""
cmdline_options_x=""
cmdline_options_z=""
cmdline_targets=""
cmdline_modulename=""
cmdline_deploypath=""
cmdline_forwards=""

# parse cmdline
cmdline="$@"
mklog debug "origin-args:[$@]"
temp=$(getopt -o "C:f:hj::l::m:d:xz" --long "" -n "mkd" -- "$@")
if [ $? != 0 ] ; then
    echo "`help`" >&2
    exit 1
fi
eval set -- "$temp"
mklog debug "parsed-args:[$temp]"
while true
do
    case "$1" in
        -C) cmdline_makedir="$2" ;  shift 2 ;;
        -f) cmdline_makefile="$2" ; shift 2 ;;
        -h) echo "`help`" >&2; exit 0;;
        -j) cmdline_options="$cmdline_options -j$2"; shift 2;;
        -l) cmdline_options="$cmdline_options -l$2"; shift 2;;
        -m) cmdline_modulename="$2"; shift 2;;
        -d) cmdline_deploypath="$2"; shift 2;;
        -x) cmdline_options_x="-x"; shift 1;;
        -z) cmdline_options_z="-z"; shift 1;;
        --) shift ; break ;;
        *)  echo "parse options error!" >&2 ; exit 1 ;;
    esac
done
cmdline_targets="$@"
mklog debug "cmdline_makedir:[$cmdline_makedir], cmdline_makefile:[$cmdline_makefile]," \
            "cmdline_options:[$cmdline_options], cmdline_targets:[$cmdline_targets]," \
            "cmdline_options_x:[$cmdline_options_x], cmdline_options_z:[$cmdline_options_z]," \
            "cmdline_modulename:[$cmdline_modulename], cmdline_deploypath:[$cmdline_deploypath]"

# cmdline forwards
if [ -n "$cmdline_modulename" ] ; then
    cmdline_forwards="$cmdline_forwards -m$cmdline_modulename"
fi
if [ -n "$cmdline_deploypath" ] ; then
    cmdline_forwards="$cmdline_forwards -d$cmdline_deploypath"
fi
mklog debug "cmdline_forwards:[$cmdline_forwards]"

# (using $makefile to make and) deploy $targets from $makedir
targets="$cmdline_targets"
makedir="$cmdline_makedir"
makefile="$cmdline_makefile"

# wrapper
succ_exit() { [ -n "$makedir" ] && echo "mkd: Leaving directory '$cmdline_makedir'"; exit 0; }
fail_exit() { [ -n "$makedir" ] && echo "mkd: Leaving directory '$cmdline_makedir'"; exit 1; }

# go into make directory if need
if [ -n "$makedir" ] ; then
    if [ ! -d "$makedir" ] ; then
        mklog error "check directory fail, directory:[$makedir]"
        exit 1
    fi

    echo "mkd: Entering directory '$makedir'"
    cd "$makedir" || fail_exit
fi

# get makefile from cmdline(by user) or make(auto load)
if [ -z "$makefile" ] ; then
    makefile="`make $cmdline_options $cmdline_targets -n -p 2>/dev/null \
               | grep '^MAKEFILE_LIST' \
               | head -n1 \
               | awk '{printf $3}'`"
    if [ -z "$makefile" -a -z "$targets" ] ; then
        mklog error "none-make targets not found, cmdline:[$cmdline]," \
                    "makefile:[$makefile], targets:[$targets]"
        fail_exit
    fi
fi
mklog debug "makefile:[$makefile], targets:[$targets]"

if [ -n "$makefile" ] ; then
    # check makefile
    if [ ! -f "$makefile" ] ; then
        mklog error "check makefile fail, makefile:[$makefile]"
        fail_exit
    fi

    # maybe mkd for sub directorys
    submakedirs="`make -f $makefile -n -p 2>/dev/null | grep '^DIRS =' | tail -n1 | cut -c8-`"
    mklog debug "submakedirs=$submakedirs"
    if [ -n "$submakedirs" ] ; then
        for subdir in $submakedirs
        do
            if [ "${subdir:0:1}" = "/" ] ; then
                mkd -C $subdir $cmdline_options
            else
                mkd -C $makedir/$subdir $cmdline_options
            fi
        done
        mklog debug "mkd for directorys end"
        succ_exit
    fi

    # get targets from cmdline
    if [ -z "$targets" ] ; then
        # default targets from $output var in makefile
        output="`mkm get config output-name OUTPUT`"
        length="$((${#output} + 4))"
        targets="`make -f $makefile -n -p 2>/dev/null | grep "^$output =" | cut -c$length-`"
        if [ -z "$targets" ] ; then
            mklog error "need-make targets not found, cmdline:[$cmdline]," \
                        "output-name:[$output], length:[$length]"
            fail_exit
        fi
    fi
    mklog debug "targets(need make):[$targets]"

    # make targets from cmdline(by user) or make(auto load)
    mk -f $makefile $cmdline_options $cmdline_options_x $cmdline_options_z $targets
    if [ $? -ne 0 ] ; then
        fail_exit
    fi
fi

# deploy targets
for target in $targets
do
    # .so target is specail
    if expr match "$target" ".*\.so$" >/dev/null 2>&1 ; then
        if ! expr match "$target" "^lib" >/dev/null 2>&1 ; then
            target="lib$target"
        fi
    fi
    # .a target is specail
    if expr match "$target" ".*\.a$" >/dev/null 2>&1 ; then
        if ! expr match "$target" "^lib" >/dev/null 2>&1 ; then
            target="lib$target"
        fi
    fi

    # find target's module
    # or use user-defined module-name/deploy-path in cmdline
    modulename="$cmdline_modulename";
    deploypath="$cmdline_deploypath";
    if [ -z "$modulename" -o -z "$deploypath" ] ; then
        # find modulename or deploypath by target
        eval `mkm find target $target \
              | awk '{printf "modulename=%s; deploypath=%s;" \
                             ,           $1,            $2}'`
        if [ -z "$modulename" -o -z "$deploypath" ] ; then
            eval `mkm find targetreg $target \
                 | awk '{printf "modulename=%s; deploypath=%s;" \
                                ,           $1,            $2}'`
            if [ -z "$modulename" -o -z "$deploypath" ] ; then
                mklog error "module not found, target:[$target]"
                continue
            fi
            mklog debug "module found in targetreg, target:[$target]"
        fi 

        # use user-defined module name (from cmdline)
        if [ -n "$cmdline_modulename" ] ; then
            modulename="$cmdline_modulename"
        fi

        # use user-defined deploy path (from cmdline)
        if [ -n "$cmdline_deploypath" ] ; then
            deploypath="$cmdline_deploypath"
        fi
    fi

    # find module's info
    mtype=""; username=""; rsapkey=""; hostname=""; hostport="";
    eval `mkm find module $modulename \
          | awk '{printf "mtype=%s; username=%s; rsapkey=%s; hostname=%s; hostport=%s;" \
                         ,     $2,          $3,         $4,          $5,          $6}'`
    mklog debug "target:[$target], module:[$modulename], mtype:[$mtype]," \
                "username:[$username], rsa-private:[$rsapkey]," \
                "hostname:[$hostname], hostport:[$hostport]," \
                "deploypath:[$deploypath], makedir:[$makedir]"
    if [   "$mtype" != "rsa"    \
        -a "$mtype" != "local"  \
        -a "$mtype" != "passwd" \
        -a "$mtype" != "custom" ] ; then
        mklog error "module type unsupported, type:[$mtype]," \
                    "supported type:[rsa, local, passwd, custom], target:[$target]"
        continue
    fi

    # do deploy
    mkrun $cmdline_forwards pre-deploy $target 2>/dev/null
    case "$mtype" in
        rsa)
            if [   -z "$username" -o -z "$rsapkey" \
                -o -z "$hostname" -o -z "$hostport" -o -z "$deploypath" ]
            then
                mklog error "rsa module not ok, target:[$target], module:[$modulename]"
                continue
            fi
            scp -C -i $rsapkey -P $hostport "$makedir/$target" $username@$hostname:$deploypath/
            ;;
        local)
            cp -v "$makedir/$target" $deploypath/
            ;;
        passwd)
            if [   -z "$username" -o -z "$rsapkey" \
                -o -z "$hostname" -o -z "$hostport" -o -z "$deploypath" ]
            then
                mklog error "passwd module not ok, target:[$target], module:[$modulename]"
                continue
            fi
            mkscpto "$makedir/$target" $username $rsapkey $hostname $hostport $deploypath/
            ;;
        custom)
            # custom deployer
            customdeployer="`mkm get config custom-deployer`"
            if [ -z "$customdeployer" ] ; then
                mklog error "custom deployer not found, target:[$target], module:[$modulename]"
                continue
            fi
            mklog debug "custom-deployer:[$customdeployer]"

            # run deployer
            export MKX_CUSTOM_TARGETPATH="$makedir/$target" \
                   MKX_CUSTOM_TARGETNAME="$target"          \
                   MKX_CUSTOM_DEPLOYPATH="$deploypath/"     \
                   MKX_CUSTOM_CONFIG1="$modulename"         \
                   MKX_CUSTOM_CONFIG2="$mtype"              \
                   MKX_CUSTOM_CONFIG3="$username"           \
                   MKX_CUSTOM_CONFIG4="$rsapkey"            \
                   MKX_CUSTOM_CONFIG5="$hostname"           \
                   MKX_CUSTOM_CONFIG6="$hostport"
            eval "$customdeployer"
            unset  MKX_CUSTOM_TARGETPATH \
                   MKX_CUSTOM_TARGETNAME \
                   MKX_CUSTOM_DEPLOYPATH \
                   MKX_CUSTOM_CONFIG1    \
                   MKX_CUSTOM_CONFIG2    \
                   MKX_CUSTOM_CONFIG3    \
                   MKX_CUSTOM_CONFIG4    \
                   MKX_CUSTOM_CONFIG5    \
                   MKX_CUSTOM_CONFIG6    
            ;;
        *)
            mklog error "module type unsupported, type:[$mtype]," \
                        "supported type:[rsa, local, passwd, custom]"
            continue
            ;;
    esac
    mkrun $cmdline_forwards post-deploy $target 2>/dev/null
done

# all done
succ_exit
